import axios from 'axios';
import _ from 'lodash';
import qs from 'qs';

function validateParams(p, rule) {
  return new Promise((resolve, reject) => {
    let errors = [];
    rule.forEach((item) => {
      let { key, required, default: defaultValue, opts } = item;

      if (!_.has(p, key)) {
        if (required) {
          errors.push(`${key} 是必填项，但未在参数中找到。`);
        } else {
          // 如果不是必填项，则使用默认值填充
          // p[key] = defaultValue;//当前不需要做这步
        }
      } else {
        let arr = _.keys(opts).map((v) => {
          let nv = Number(v);
          return Number.isNaN(nv) ? v : nv;
        });
        // 如果key存在于p中，但opts有定义，并且p[key]不在opts的keys中，则报错
        if (arr.length > 0 && !arr.includes(p[key])) {
          errors.push(`${key} 的值 ${p[key]} 不在允许的选项中。`);
        }
      }
    });
    // 如果存在错误，返回错误信息数组；否则返回null表示验证通过
    if (errors.length > 0) {
      reject(new Error(errors.join('\n')));
    } else {
      resolve(p);
    }
  });
}

let axios3th = axios.create({
  timeout: 1000 * 10,
});

axios3th.defaults.headers.post['Content-Type'] =
  'Content-Type:application/x-www-form-urlencoded; charset=UTF-8'; //预防跨域

// 如果使用全局loading
// let loading;
// function openLoading() {loading = Loading.service({lock: true,background: 'rgba(255,255,255,0.6)'});}
// function closeLoading() {loading.close()}
// 不过实际业务不怎么待见全局loading（无论它们知道与否）

// 添加请求拦截器
axios3th.interceptors.request.use(
  (config) => {
    // 在发送请求之前做些什么
    // 如果使用全局loading，可以在这里open，不过……
    return config;
  },
  (error) => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);
// 添加响应拦截器
axios3th.interceptors.response.use(
  function(response) {
    // 如果使用全局loading，可以在这里close，不过……
    // 对响应数据做点什么
    if (response.config.responseType == 'blob') {
      return response; //对Blob不做脱壳处理
    }
    if (response.status === 200) {
      return response.data; //对明确的200做脱壳处理
    }
    // http://www.axios-js.com/zh-cn/docs/#%E5%93%8D%E5%BA%94%E7%BB%93%E6%9E%84 官网的响应结构
    return response;
  },
  function(error) {
    console.log(error);
    // 对响应错误做点什么
    if (error && error.response) {
      switch (error.response.status) {
        case 400:
          error.message = '请求错误(400)';
          break;
        case 401:
          this.$router.push('/login');
          break;
        case 403:
          error.message = '拒绝访问(403)';
          break;
        case 404:
          error.message = '请求出错(404)';
          break;
        case 408:
          error.message = '请求超时(408)';
          break;
        case 500:
          error.message = '服务器错误(500)';
          break;
        case 501:
          error.message = '服务未实现(501)';
          break;
        case 502:
          error.message = '网络错误(502)';
          break;
        case 503:
          error.message = '服务不可用(503)';
          break;
        case 504:
          error.message = '网络超时(504)';
          break;
        case 505:
          error.message = 'HTTP版本不受支持(505)';
          break;
        default:
          error.message = `连接出错(${error.response.status})!`;
      }
    } else {
      error.message = '连接服务器失败!';
    }
    return Promise.reject(error);
  }
);

let axiosWithToken = axios.create({
  timeout: 1000 * 10,
});
axiosWithToken.defaults.headers.post['Content-Type'] =
  'Content-Type:application/x-www-form-urlencoded; charset=UTF-8'; //预防跨域

let HS_Token = null;

axiosWithToken.interceptors.request.use(
  (config) => {
    if (HS_Token) {
      config.headers.Authorization = `${HS_Token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
axiosWithToken.interceptors.response.use(
  function(response) {
    if (response.status === 200) {
      return response.data; //对明确的200做脱壳处理
    }
  },
  function(error) {
    if (error.response && error.response.status === 401) {
      // 请求返回401，请求新的token
      let url = 'http://dfqs.chenxi618.com:9898/hq_common/GetTokenC2';
      return axios
        .get(url, {})
        .then((response) => {
          HS_Token = response.value1;
          // localStorage.setItem('HS_Token', response.data.value1);
          let config = Object.assign(error.config, {
            headers: { Authorization: HS_Token },
          });
          return axios(config);
        })
        .catch((error) => {
          // 获取新token失败的处理
          console.error('获取新token失败', error);
          return Promise.reject(error);
        });
    }

    return Promise.reject(error);
  }
);

let api3th = {
  getQiniuToken: function() {
    return axios3th.get(
      `http://ewapi.chenxi18.com:82/api/Material/GetQiNiuToken`
    );
  }, //获取七牛Token,不在actions中再封装

  uploadQiniu: function(params, data) {
    return axios3th.post(`http://upload.qiniup.com`, data, {
      headers: { 'Content-Type': 'multipart/form-data' },
    });
  }, //上传图片到七牛

  get恒生Token: function(params) {
    // 官方文档：https://www.hs.net/openplat-doc/apiDoc?apiId=f269b9be7efe4de08c7831325eb56968
    // 但这个文档可参考内容不足，详询安卓前端获取“按键精灵”相关内容
    let url = 'http://dfqs.chenxi618.com:9898/hq_common/GetTokenC2';
    return axios3th.get(url, { params: params });
    // 不过该接口没有使用，因为已集成到axiosWithToken拦截器中了。
  },

  // 恒生的标准化定义中，
  // 标准字段的数据类型分为字符C，字符串C，整数N和浮点数N，举例：
  // C1表示字符，字符规定为1位；C200表示为字符串，长度为200；
  // N64表示整数，长度为64位；N64.1表示浮点数，长度为64位，精度为1；

  getKline: function(params) {
    // https://www.hs.net/openplat-doc/apiDoc?apiId=0613999fe5b84fba95c90577f0d02b5c
    const url = `https://open.hs.net/quote/v1/kline`;
    let rule = [
      {
        key: 'get_type',
        required: true,
        default: null,
        readme: '查找类别',
        opts: { offset: '按偏移查找', range: '按日期区间查找' },
      },
      {
        key: 'prod_code',
        required: true,
        default: null,
        readme: '产品代码',
        opts: {},
      },
      {
        key: 'candle_period',
        required: true,
        default: null,
        readme: 'K线周期',
        opts: {
          1: '1分钟K线',
          2: '5分钟K线',
          3: '15分钟K线',
          4: '30分钟K线',
          5: '60分钟K线',
          6: '日K线',
          7: '周K线',
          8: '月K线',
          9: '年K线',
        },
      },
      {
        key: 'candle_mode',
        required: false,
        default: 0,
        readme: 'K 线模式',
        opts: {
          0: '原始K线',
          1: '前复权K线',
          2: '后复权K线',
        },
      },
      {
        key: 'fields',
        required: false,
        default: null,
        readme: '字段集合',
        opts: {},
      }, //默认返回 min_time,open_px,high_px,low_px ,close_px,business_amount,business_balance；如需返回更多，请传字段参数名，多个参数用英文逗号隔开
      {
        key: 'search_direction',
        required: false,
        default: 1,
        readme: '搜索方向',
        opts: {
          1: '表示向前查找（默认值）',
          2: '表示向后查找。 仅在 get_type=offset 时有效。',
        },
      },
      {
        key: 'date',
        required: false,
        default: null, //默认为当前日期
        readme:
          '日期:不输入默认为当前日期；请求日 K 线时，如果输入日期，不返回该日期的 K 线 get_type=offset 时有',
        opts: {},
      },
      {
        key: 'min_time',
        required: false,
        default: null,
        readme: `分时分钟时间(HHMM)
            分钟K线的时间HHMM,对于短周期K线类型使用(1min,5min 等)，不填写表示最新的市场时间，若填写必须同时填写date字段。
            请求分钟K线时，如果输入该字段，不返回输入分钟的K线
            仅在 get_type=offset且candle_period=1~5（分钟 K 线）时有效。
          `,
        opts: {},
      },
      {
        key: 'data_count',
        required: false,
        default: 10,
        readme: '数据个数',
        opts: {},
      }, //需要取得的 K 线的根数，如果该字段不存在，取值范围[1, 1000]，默认为 10 个。 仅在 get_type=offset 时有效。
      {
        key: 'start_date',
        required: false,
        default: null,
        readme: '开始日期',
        opts: {},
      },
      {
        key: 'end_date',
        required: false,
        default: null,
        readme: '截止日期',
        opts: {},
      },
    ];
    return validateParams(params, rule).then((p) => {
      return axiosWithToken.get(url, {
        params,
        paramsSerializer: function(params) {
          return qs.stringify(params, {
            arrayFormat: 'repeat',
          });
        },
      });
    });
  },
  get市场值by股票代码: function(params) {
    // 官方文档：https://www.hs.net/openplat-doc/apiDoc?apiId=f269b9be7efe4de08c7831325eb56968
    // 但这个文档可参考内容不足，详询安卓前端获取“按键精灵”相关内容
    let url = 'https://open.hs.net/quote/v1/wizard';
    // 该接口传入参数有三个，一个必须 prod_code ，一个比较重要 en_finance_mic ，一个无关紧要 data_count
    // 目前打算让使用者只传入 prod_code
    // 固定 en_finance_mic
    Object.assign(params, {
      en_finance_mic: 'SS.ESA,SS.N,SS.KSH,SZ.ESA,SZ.N,SZ.KSH',
    });
    let rule = [
      {
        key: 'prod_code',
        required: true,
        default: null,
        readme: '产品代码',
        opts: {},
      },
      {
        key: 'en_finance_mic',
        required: false,
        default: null,
        readme: '交易所识别码集合',
        opts: {},
        // 多个交易所识别码,逗号(,)分割。 如：
        // finance_mic=SS,SZ表示优先查询上交所和深交所的代码，且按照参数的先后顺序优先查找
        // 注：返回结果不按查找的市场顺序排序
      },
      {
        key: 'data_count',
        required: false,
        default: 50,
        readme: '数据个数',
        opts: {},
      },
    ];
    return validateParams(params, rule).then((p) => {
      return axiosWithToken.get(url, {
        params,
        paramsSerializer: function(params) {
          return qs.stringify(params, {
            arrayFormat: 'repeat',
          });
        },
      });
    });
  },
  get实时信息byProdCodeMarker: function(params) {
    // 接口文档：https://www.hs.net/openplat-doc/apiDoc?apiId=b6f1842d42a44954bfb4964d769a94e5
    // 接口源于下面的 “获取股票实时信息” 接口重写
    let url = 'http://dfqs.chenxi618.com:9898/HSCoreV2/GetHSStock';
    return axios3th
      .get(url, {
        params: params,
        paramsSerializer: function(params) {
          params.en_prod_code = params.en_prod_code.join(',');
          params.fields = params.fields.join(',');
          return qs.stringify(params, {
            arrayFormat: 'repeat',
          });
        },
      })
      .then((res) => {
        let snapshot = _.get(res, 'data.snapshot', false);
        if (!snapshot) return Promise.reject('no data');
        const FIELDS = 'fields';
        let data = _.filter(snapshot, (v, k) => {
          return k !== FIELDS && _.isArray(v);
        }).map((v) => {
          return _.zipObject(snapshot[FIELDS], v);
        });

        return Promise.resolve(data);
      });
  },

  获取股票实时信息: function(params) {
    // 接口文档：https://www.hs.net/openplat-doc/apiDoc?apiId=b6f1842d42a44954bfb4964d769a94e5
    let url = 'http://dfqs.chenxi618.com:9898/HSCoreV2/GetHSStock';
    let exSS = /^(60)\d{4}$/;
    let exSZ = /^(000|001|002|300)\d{3}$/;
    return axios3th
      .get(url, {
        params: params,
        paramsSerializer: function(params) {
          params.en_prod_code = params.en_prod_code
            .map((v) => {
              if (exSS.test(v)) return `${v}.SS`;
              if (exSZ.test(v)) return `${v}.SZ`;
              return v;
            })
            .join(',');
          params.fields = params.fields.join(',');
          return qs.stringify(params, {
            arrayFormat: 'repeat',
          });
        },
      })
      .then((res) => {
        let snapshot = _.get(res, 'data.snapshot', false);
        if (!snapshot) return Promise.reject('no data');
        const FIELDS = 'fields';
        let data = _.filter(snapshot, (v, k) => {
          return k !== FIELDS && _.isArray(v);
        }).map((v) => {
          return _.zipObject(snapshot[FIELDS], v);
        });

        return Promise.resolve(data);
      });
  },
};

export { api3th };
